﻿using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using ZSoft.PushServices.Net.DTO;
using ZSoft.PushServices.Net.Transport;
using ZSoft.PushServices.Net.Transport.Hubs;

namespace ZSoft.PushServices.Net.Services
{

    public class HubNotificationService : INotificationService
    {
        private Transport.IConnectionManager _connectionManager;
        private IHubContext _context;
        private INotificationQueueProvider _queueProvider;

        public event NoitificationSendHandler NotificationSent;
        public event NoitificationSendFailureHandler NotificationFailed;
        public event NotificationQueuedHandler NotificationQueued;

        public HubNotificationService(Transport.IConnectionManager connectionManager)
        {
            _connectionManager = connectionManager;
        }

        public HubNotificationService() : this((Transport.IConnectionManager)GlobalHost.DependencyResolver.GetService(typeof(Transport.IConnectionManager)))
        {
            this._context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
            this._queueProvider = GlobalHost.DependencyResolver.GetService(typeof(INotificationQueueProvider)) as INotificationQueueProvider;
        }

        protected void OnNotificationSent(NotificationSendEventArgs e)
        {
            this.NotificationSent?.Invoke(this, e);
        }

        protected void OnNotificationFailure(NotificationFailureEventArgs e)
        {
            this.NotificationFailed?.Invoke(this, e);
        }

        protected void OnNotificationQueued(NotificatioinQueuedEventArgs e)
        {
            this.NotificationQueued?.Invoke(this, e);
        }

        public NotificationResult NotifyDevice(string deviceId, NotificationDTO notif)
        {
            return Notify(deviceId, notif, _connectionManager.GetDeviceStatus(deviceId));
        }

        public NotificationResult Notify(string deviceId, NotificationDTO notif, DeviceStatus deviceStatus)
        {
            if (deviceStatus == DeviceStatus.Online)
            {
                return Notify(deviceId, notif);
            }
            else // enqueue notification for user for further notices
            {
                _queueProvider.Enqueue(deviceId, notif);
                return NotificationResult.Failed;
            }
        }

        private NotificationResult Notify(string deviceId, NotificationDTO notif)
        {
            try
            {
                this._context.Clients.Client(_connectionManager.GetConnectionId(deviceId)).RecieveNotification(notif);
                this.OnNotificationSent(new NotificationSendEventArgs() { DeviceId = deviceId, Notification = notif });
                return NotificationResult.Succeded;
            }
            catch (Exception ex)
            {
                this.OnNotificationFailure(new NotificationFailureEventArgs(ex) { DeviceId = deviceId, Notification = notif });
                Enqueue(deviceId, notif);

                return NotificationResult.Failed;
            }
        }

        public List<KeyValuePair<string, NotificationResult>> NotifyDevices(string appId, NotificationDTO notif)
        {
            List<KeyValuePair<string, NotificationResult>> result = new List<KeyValuePair<string, NotificationResult>>();
            _connectionManager
                .GetAppClientDeviceIds(appId)
                    .ForEach(deviceId => result.Add(new KeyValuePair<string, NotificationResult>(deviceId, this.NotifyDevice(deviceId, notif))));
            return result;
        }


        private void Enqueue(string deviceId, NotificationDTO notif)
        {
            _queueProvider.Enqueue(deviceId, notif);
            this.OnNotificationQueued(new NotificatioinQueuedEventArgs());
        }
    }
}
